//
//  KDDStorage.m
//  MengLiao
//
//  Created by xiuxin on 2022/7/15.
//  Copyright © 2022 ZHBen. All rights reserved.
//

#import "KDDStorage.h"
#import "KDDModel.h"
@implementation KDDStorage
{
    NSMutableArray<KDDSectionData *> * _sectionDataList;
}
@synthesize data = _sectionDataList;

- (instancetype)init
{
    if (self = [super init])
    {
        _sectionDataList = [NSMutableArray array];
        self.updateReceiver = self;
    }
    return self;
}

- (instancetype)initWithData:(NSArray<KDDSectionData *> *)sectionDataList
{
    if (self = [super init])
    {
//        _sectionDataList = [KDDSectionData copySectionDataList:sectionDataList];
        _sectionDataList = [NSMutableArray arrayWithArray:sectionDataList];
        self.updateReceiver = self;
    }
    return self;
}

- (void)didReceiveUpdate:(KDDUpdate *)update
{
    [_updateHandler dataStorage:self willCommitUpdate:update];
    [self commitUpdate:update];
    [_updateHandler dataStorage:self didCommitUpdate:update];
}

- (BOOL)checkReloadData:(NSArray<KDDUpdate *> *)updateList
{
    for (KDDUpdate * update in updateList)
    {
        if (update.target == KDDTargetList)
        {
            return YES;
        }
    }
    return NO;
}

- (void)didReceiveBatchUpdateList:(NSArray<KDDUpdate *> *)updateList animated:(BOOL)animated completion:(void (^)(BOOL finished))completion
{
    BOOL reloadData = [self checkReloadData:updateList];
    if (!reloadData && animated)
    {
        if ([self.updateHandler respondsToSelector:@selector(dataStorage:performBatchUpdates:completion:)])
        {
            [self.updateHandler dataStorage:self performBatchUpdates:^{
                for (KDDUpdate * update in updateList)
                {
                    [self didReceiveUpdate:update];
                }
            } completion:completion];
        }
        else
        {
            for (KDDUpdate * update in updateList)
            {
                [self didReceiveUpdate:update];
            }
            KExecuteBlock(completion,YES);
        }
    }
    else
    {
//        id<KDDUpdateHandler> updateHandler = _updateHandler;
//        _updateHandler = nil;
        KDDUpdate * modifyList = [[KDDUpdate alloc] initWithTarget:KDDTargetList updateType:KDDUpdateModify];
        [self.updateHandler dataStorage:self willCommitUpdate:modifyList];
        for (KDDUpdate * update in updateList)
        {
//            [self didReceiveUpdate:update];
            [self commitUpdate:update];
        }
        [modifyList.modelList addObjectsFromArray:self.data];
        [self.updateHandler dataStorage:self didCommitUpdate:modifyList];
//        _updateHandler = updateHandler;
        KExecuteBlock(completion,YES);
    }
}

- (void)commitUpdate:(KDDUpdate *)update toData:(NSMutableArray<KDDSectionData *> *)data
{
    if ([update isKindOfClass:[KOSUpdate class]])
    {
        for (KDDUpdate * object in [(KOSUpdate *)update updateList])
        {
            [self _commitUpdate:object toData:data];
        }
    }
    else
    {
        [self _commitUpdate:update toData:data];
    }
}

- (void)commitUpdate:(KDDUpdate *)update
{
    [self commitUpdate:update toData:_sectionDataList];
}

- (void)_commitUpdate:(KDDUpdate *)update toData:(NSMutableArray<KDDSectionData *> *)data
{
    if (update.updateType == KDDUpdateReload)
    {
        return;
    }
    NSMutableArray * dataList = nil;
    if (update.target == KDDTargetList)
    {
        [data removeAllObjects];
        [data addObjectsFromArray:update.modelList];
    }
    else if (update.target == KDDTargetSection)
    {
        dataList = data;
    }
    else if (update.target == KDDTargetCell)
    {
        dataList = data[update.section].cellModelListForUpdate;
    }
    else if (update.target == KDDTargetHeader)
    {
        data[update.section].headerModel = update.model;
    }
    else if (update.target == KDDTargetFooter)
    {
        data[update.section].footerModel = update.model;
    }
    if (dataList != nil)
    {
        if (update.updateType == KDDUpdateInsert)
        {
            [dataList insertObjects:update.modelList atIndexes:update.indexSet];
        }
        else if (update.updateType == KDDUpdateDelete)
        {
            [dataList removeObjectsAtIndexes:update.indexSet];
        }
        else if (update.updateType == KDDUpdateModify)
        {
            [dataList replaceObjectsAtIndexes:update.indexSet withObjects:update.modelList];
        }
        if (update.target == KDDTargetSection)
        {
            for (NSInteger index = update.indexSet.firstIndex; index < data.count; index++)
            {
                data[index].index = index;
            }
        }
    }
}

#pragma mark KDataDrivenStorage Method

- (KDDPosition *)positionOfModel:(NSObject *)model sectionData:(KDDSectionData *)sectionData
{
    if (sectionData.headerModel == model)
    {
        return [KDDPosition headerPosition:sectionData.index];
    }
    if (sectionData.footerModel == model)
    {
        return [KDDPosition footerPosition:sectionData.index];
    }
    if (sectionData.cellModelList.count > 0)
    {
        NSInteger index = [sectionData.cellModelList indexOfObject:model];
        if (index != NSNotFound)
        {
            return [KDDPosition cellPositionWithSection:sectionData.index index:index];
        }
    }
    return nil;
}

- (KDDPosition *)positionOfModel:(NSObject *)model inSection:(NSInteger)sectionIndex
{
    if (sectionIndex < 0 || sectionIndex >= self.data.count)
    {
        for (KDDSectionData * sectionData in self.data)
        {
            KDDPosition * position = [self positionOfModel:model sectionData:sectionData];
            if (position != nil)
            {
                return position;
            }
        }
        return nil;
    }
    else
    {
        return [self positionOfModel:model sectionData:self.data[sectionIndex]];
    }
}

- (NSArray<KDDPosition *> *)positionsOfModels:(NSArray *)modelList notFound:(NSMutableArray **)notFound
{
    NSMutableArray<KDDPosition *> * positionList = [NSMutableArray arrayWithCapacity:modelList.count];
    for (NSObject * model in modelList)
    {
        KDDPosition * position = nil;
        for (KDDSectionData * sectionData in self.data)
        {
            position = [self positionOfModel:model sectionData:sectionData];
            if (position != nil)
            {
                break;
            }
        }
        if (position != nil)
        {
            [positionList addObject:position];
        }
        else if (notFound != nil)
        {
            [*notFound addObject:model];
        }
    }
    return positionList;
}

- (NSArray<KDDPosition *> *)positionsOfModels:(NSArray *)modelList section:(NSInteger)section notFound:(NSMutableArray **)notFound
{
    if (self.data.count <= section)
    {
        return nil;
    }
    NSMutableArray<KDDPosition *> * positionList = [NSMutableArray arrayWithCapacity:modelList.count];
    KDDSectionData * sectionData = self.data[section];
    for (NSObject * model in modelList)
    {
        KDDPosition * position = [self positionOfModel:model sectionData:sectionData];
        if (position != nil)
        {
            [positionList addObject:position];
        }
        else if (notFound != nil)
        {
            [*notFound addObject:model];
        }
    }
    return positionList;
}

- (NSObject *)modelAtPosition:(KDDPosition *)position
{
    if (position.section >= self.data.count)
    {
        return nil;
    }
    KDDSectionData * sectionData = self.data[position.section];
    if (position.viewType == KDDViewTypeCell || position.viewType == KDDViewTypeDecoration)
    {
        return kObjectFromArray(sectionData.cellModelList, position.index);
    }
    else if (position.viewType == KDDViewTypeHeader)
    {
        return sectionData.headerModel;
    }
    else if (position.viewType == KDDViewTypeFooter)
    {
        return sectionData.footerModel;
    }
    return nil;
}

- (NSArray *)modelsAtPositions:(NSArray<KDDPosition *> *)positionList notFound:(NSMutableArray<KDDPosition *> **)notFound
{
    NSMutableArray * modelList = [NSMutableArray arrayWithCapacity:positionList.count];
    for (KDDPosition * position in positionList)
    {
        NSObject * model = [self modelAtPosition:position];
        if (model != nil)
        {
            [modelList addObject:model];
        }
        else if (notFound != nil)
        {
            [*notFound addObject:position];
        }
    }
    return modelList;
}

- (void)setIndexTitle:(NSString *)indexTitle forSection:(NSInteger)section
{
    [kObjectFromArray(self.data, section) setIndexTitle:indexTitle];
}

#pragma mark KDataDrivenOperation Methods

- (void)deleteSectionsAtIndexes:(NSIndexSet *)indexSet
{
    if (indexSet.count == self.data.count)
    {
        [self modifyData:@[[KDDSectionData new]]];
    }
    else
    {
        [super deleteSectionsAtIndexes:indexSet];
    }
}

- (void)switchModelAtPositions:(NSArray<KDDPosition *> *)positionList1 toPositions:(NSArray<KDDPosition *> *)positionList2
{
    NSArray * modelList1 = [self modelsAtPositions:positionList1 notFound:nil];
    NSArray * modelList2 = [self modelsAtPositions:positionList2 notFound:nil];
    [self modifyModelsAtPositions:positionList1 withModels:modelList2];
    [self modifyModelsAtPositions:positionList2 withModels:modelList1];
}
//Data Operation
- (void)modifyData:(NSArray<KDDSectionData *> *)sectionDataList
{
    [super modifyData:sectionDataList.count > 0 ? sectionDataList : @[[KDDSectionData new]]];
}

// Other
- (void)splitSection:(NSInteger)sectionIndex rowOffset:(NSInteger)rowOffset
{
    KDDSectionData * section = self.data[sectionIndex];
    NSMutableArray<KDDPosition *> * positionList = [NSMutableArray array];
    KDDSectionData * splitSection = [KDDSectionData copySectionData:section];
    splitSection.headerModel = nil;
    if (rowOffset > 0)
    {
        [splitSection.cellModelListForUpdate removeObjectsInRange:NSMakeRange(0, rowOffset)];
    }
    if (splitSection.cellModelList.count > 0)
    {
        NSIndexSet * indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(rowOffset, splitSection.cellModelList.count)];
        [positionList addObjectsFromArray:[KDDPosition cellPositionList:indexSet section:sectionIndex]];
    }
    if (splitSection.footerModel != nil)
    {
        [positionList addObject:[KDDPosition footerPosition:sectionIndex]];
    }
    [self deleteModelsAtPositions:positionList];
    [self insertSections:@[splitSection] atIndexes:[NSIndexSet indexSetWithIndex:sectionIndex + 1]];
}

- (void)connectSectionsInRange:(NSRange)range
{
    NSRange deleteRange = range;
    deleteRange.location += 1;
    deleteRange.length -= 1;
    
    KDDSectionData * firstSection = self.data[range.location];
    NSArray<KDDSectionData *> * sectionList = [self.data subarrayWithRange:deleteRange];
    
    NSMutableArray * modelList = [NSMutableArray array];
    NSMutableArray<KDDPosition *> * positionList = [NSMutableArray arrayWithCapacity:modelList.count + 1];
    for (KDDSectionData * section in sectionList)
    {
        if (section.cellModelList.count > 0)
        {
            [modelList addObjectsFromArray:section.cellModelList];
        }
    }
    if (modelList.count > 0)
    {
        [positionList addObjectsFromArray:[KDDPosition cellPositionList:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(firstSection.cellModelList.count, modelList.count)] section:range.location]];
    }
    if (sectionList.lastObject.footerModel != nil)
    {
        [modelList addObject:sectionList.lastObject.footerModel];
        [positionList addObject:[KDDPosition footerPosition:sectionList.lastObject.index]];
    }
    [self deleteSectionsAtIndexes:[NSIndexSet indexSetWithIndexesInRange:deleteRange]];
    if (modelList.count > 0)
    {
        [self insertModels:modelList atPositions:positionList];
    }
}

- (NSArray<KDDPosition *> *)positionListOfSection:(KDDSectionData *)sectionData sectionIndex:(NSInteger)sectionIndex rowOffset:(NSInteger)rowOffset modelList:(NSMutableArray **)modelList
{
    NSMutableArray<KDDPosition *> * positionList = [NSMutableArray array];
    if (sectionData.headerModel != nil)
    {
        if (modelList != nil)
        {
            [*modelList addObject:sectionData.headerModel];
        }
        [positionList addObject:[KDDPosition headerPosition:sectionIndex]];
    }
    if (sectionData.cellModelList.count > 0)
    {
        if (modelList != nil)
        {
            [*modelList addObjectsFromArray:sectionData.cellModelList];
        }
        NSIndexSet * indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(rowOffset, sectionData.cellModelList.count)];
        [positionList addObjectsFromArray:[KDDPosition cellPositionList:indexSet section:sectionIndex]];
    }
    if (sectionData.footerModel != nil)
    {
        if (modelList != nil)
        {
            [*modelList addObject:sectionData.footerModel];
        }
        [positionList addObject:[KDDPosition footerPosition:sectionIndex]];
    }
    return positionList;
}

- (void)insertSectionData:(KDDSectionData *)sectionData inSection:(NSInteger)sectionIndex rowOffset:(NSInteger)rowOffset
{
    if (self.data.count == 0)
    {
        [self insertSections:@[sectionData] atIndexes:[NSIndexSet indexSetWithIndex:0]];
        return;
    }
    NSMutableArray<NSObject *> * modelList = [NSMutableArray array];
    NSArray<KDDPosition *> * positionList = [self positionListOfSection:sectionData sectionIndex:sectionIndex rowOffset:rowOffset modelList:&modelList];
    if (positionList.count > 0)
    {
        [self insertModels:modelList atPositions:positionList];
    }
}

@end
